/*-------------------------------------------------------------------------
 *
 * pg_control.h
 *	  系统控制文件 "pg_control" 不是堆关系。
 *	  然而，我们在此定义它以便记录格式。
 *
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * src/include/catalog/pg_control.h
 *
 *-------------------------------------------------------------------------
 */
#ifndef PG_CONTROL_H
#define PG_CONTROL_H

#include "access/transam.h"
#include "access/xlogdefs.h"
#include "pgtime.h"				/* 针对 pg_time_t */
#include "port/pg_crc32c.h"


/* 此 pg_control 格式的版本标识符 */
#define PG_CONTROL_VERSION	1300

/* 非ce密钥长度，见下文 */
#define MOCK_AUTH_NONCE_LEN		32

#ifdef FDD
/* TDE 所用表数据加密密钥被加密存储的长度 */
#define TDE_ENCRYPT_TABLE_KEY_LEN		32
#endif


/*
 * 检查点 XLOG 记录的主体。因为我们在 pg_control 中保留
 * 最后一个副本以备灾难恢复，所以在此声明它。
 * 更改此结构需要提高 PG_CONTROL_VERSION。
 */
typedef struct CheckPoint
{
	XLogRecPtr	redo;			/* 当我们开始创建检查点时可用的下一个 RecPtr
								 * （即 REDO 起始点） */
	TimeLineID	ThisTimeLineID; /* 当前 TLI */
	TimeLineID	PrevTimeLineID; /* 如果该记录开始一个新的时间线，则为上一个 TLI
								 * （否则等于 ThisTimeLineID） */
	bool		fullPageWrites; /* 当前 full_page_writes */
	FullTransactionId nextXid;	/* 下一个空闲事务 ID */
	Oid			nextOid;		/* 下一个空闲 OID */
	MultiXactId nextMulti;		/* 下一个空闲 MultiXactId */
	MultiXactOffset nextMultiOffset;	/* 下一个空闲 MultiXact 偏移量 */
	TransactionId oldestXid;	/* 集群范围内的最小 datfrozenxid */
	Oid			oldestXidDB;	/* 具有最小 datfrozenxid 的数据库 */
	MultiXactId oldestMulti;	/* 集群范围内的最小 datminmxid */
	Oid			oldestMultiDB;	/* 最小 datminmxid 的数据库 */
	pg_time_t	time;			/* 检查点的时间戳 */
	TransactionId oldestCommitTsXid;	/* 具有有效提交
										 * 时间戳的最旧 Xid */
	TransactionId newestCommitTsXid;	/* 具有有效提交
										 * 时间戳的最新 Xid */

	/*
	 * 最旧的正在运行的 XID。仅在从在线检查点初始化热备用
	 * 模式时需要它，因此我们只在 wal_level 为 replica 时为
	 * 在线检查点计算它。否则设置为 InvalidTransactionId。
	 */
	TransactionId oldestActiveXid;
} CheckPoint;

/* XLOG rmgr 的 XLOG 信息值 */
#define XLOG_CHECKPOINT_SHUTDOWN		0x00
#define XLOG_CHECKPOINT_ONLINE			0x10
#define XLOG_NOOP						0x20
#define XLOG_NEXTOID					0x30
#define XLOG_SWITCH						0x40
#define XLOG_BACKUP_END					0x50
#define XLOG_PARAMETER_CHANGE			0x60
#define XLOG_RESTORE_POINT				0x70
#define XLOG_FPW_CHANGE					0x80
#define XLOG_END_OF_RECOVERY			0x90
#define XLOG_FPI_FOR_HINT				0xA0
#define XLOG_FPI						0xB0
/* 0xC0 在 Postgres 9.5-11 中使用 */
#define XLOG_OVERWRITE_CONTRECORD		0xD0


/*
 * 系统状态指示符。请注意，这存储在 pg_control 中；如果您更改
 * 它，必须提高 PG_CONTROL_VERSION
 */
typedef enum DBState
{
	DB_STARTUP = 0,
	DB_SHUTDOWNED,
	DB_SHUTDOWNED_IN_RECOVERY,
	DB_SHUTDOWNING,
	DB_IN_CRASH_RECOVERY,
	DB_IN_ARCHIVE_RECOVERY,
	DB_IN_PRODUCTION
} DBState;

/*
 * pg_control 的内容。
 */

typedef struct ControlFileData
{
	/*
	 * 唯一的系统标识符 --- 以确保我们将 xlog 文件与
	 * 生成它们的安装程序匹配。
	 */
	uint64		system_identifier;

	/*
	 * 版本标识符信息。保持这些字段在相同的偏移量，
	 * 特别是 pg_control_version；如果它们移动则不会真正有用。
	 * （出于历史原因，它们必须在文件中的第 8 字节
	 * 而不是位于文件前面。）
	 *
	 * pg_control_version 标识 pg_control 本身的格式。
	 * catalog_version_no 标识系统目录的格式。
	 *
	 * 在单独的文件中还有其他版本标识符；例如，WAL 日志
	 * 包含每页的魔术数字，可以作为 WAL 日志的版本提示。
	 */
	uint32		pg_control_version; /* PG_CONTROL_VERSION */
	uint32		catalog_version_no; /* see catversion.h */

	/*
	 * 系统状态数据
	 */
	DBState		state;			/* 参见上面的枚举 */
	pg_time_t	time;			/* 最后一次 pg_control 更新的时间戳 */
	XLogRecPtr	checkPoint;		/* 最后检查点记录指针 */

	CheckPoint	checkPointCopy; /* 最后检查点记录的副本 */

	XLogRecPtr	unloggedLSN;	/* 当前虚假的 LSN 值，用于未记录的关系 */

	/*
	 * 这两个值决定了在启动之前我们必须恢复到的最小点：
	 *
	 * minRecoveryPoint 在我们结束归档恢复期间刷新数据更改时更新为最新的重放 LSN。这可以防止在开始归档恢复后中止，并以早期停止位置重新启动。如果我们已经将 WAL 记录 X 的数据更改刷新到磁盘，则在再次到达 X 之前我们不能启动。当不进行归档恢复时为零。
	 *
	 * backupStartPoint 是备份开始检查点的重做指针，如果我们正在从在线备份恢复且尚未达到备份结束。达到备份结束时重置为零，在那之前我们不能启动。否则只需要一个布尔值，但我们使用重做指针作为交叉检查，以确保我们看到的备份结束记录与我们恢复的基础备份对应。
	 *
	 * backupEndPoint 是备份结束位置，如果我们正在从从备用中获取的在线备份恢复且尚未达到备份结束。它初始化为上次备份的 pg_control 中的最小恢复点。达到备份结束时重置为零，在那之前我们不能启动。
	 *
	 * 如果 backupEndRequired 为真，我们可以确定我们正在从备份恢复，并且必须在安全启动之前看到备份结束记录。
	 */
	XLogRecPtr	minRecoveryPoint;
	TimeLineID	minRecoveryPointTLI;
	XLogRecPtr	backupStartPoint;
	XLogRecPtr	backupEndPoint;
	bool		backupEndRequired;

	/*
	 * 确定 WAL 是否可以用于归档或热备用的参数设置。
	 */
	int			wal_level;
	bool		wal_log_hints;
	int			MaxConnections;
	int			max_worker_processes;
	int			max_wal_senders;
	int			max_prepared_xacts;
	int			max_locks_per_xact;
	bool		track_commit_timestamp;

	/*
	 * 此数据用于检查数据库和后端可执行文件的硬件架构兼容性。我们不需要明确检查字节序，因为 pg_control 版本必然对不同字节序的机器看起来是错误的，但我们确实需要关注 MAXALIGN 和浮点格式。（注意：存储布局名义上也依赖于 SHORTALIGN 和 INTALIGN，但在实践中这些在所有感兴趣的架构上都是相同的。）
	 *
	 * 仅测试一个双精度值并不是浮点兼容性的可靠测试，但它能够捕捉到大多数情况。
	 */
	uint32		maxAlign;		/* 元组的对齐要求 */
	double		floatFormat;	/* 常量 1234567.0 */
#define FLOATFORMAT_VALUE	1234567.0

	/*
	 * 此数据用于确保该数据库的配置与后端可执行文件兼容。
	 */
	uint32		blcksz;			/* 此数据库的数据块大小 */
	uint32		relseg_size;	/* 大关系的每个段的块数 */

	uint32		xlog_blcksz;	/* WAL 文件内的块大小 */
	uint32		xlog_seg_size;	/* 每个 WAL 段的大小 */

	uint32		nameDataLen;	/* 目录名称字段宽度 */
	uint32		indexMaxKeys;	/* 索引中的最大列数 */

	uint32		toast_max_chunk_size;	/* TOAST 表中的块大小 */
	uint32		loblksize;		/* pg_largeobject 中的块大小 */

	bool		float8ByVal;	/* float8, int8 等按值传递？ */

#ifdef FDD
	int		encrypted;
	uint64		chkseckey;//结合system_identifier和秘钥判断秘钥是否正确
	unsigned char	tde_encrypt_tablekey[TDE_ENCRYPT_TABLE_KEY_LEN];/* 保存 TDE 所用表数据加密密钥被加密后的私钥 */
#endif //FDD

	/* 数据页是否由校验和保护？如果没有校验和版本则为零 */
	uint32		data_checksum_version;

	/*
	 * 随机随机数，用于需要基于集群唯一值（例如，早期阶段失败的 SASL 交换）的身份验证请求。
	 */
	char		mock_authentication_nonce[MOCK_AUTH_NONCE_LEN];

	/* 以上所有内容的 CRC ... 必须是最后的！ */
	pg_crc32c	crc;
} ControlFileData;

/*
 * sizeof(ControlFileData) 的最大安全值。为了可靠性，pg_control 更新是原子写入至关重要。这通常意味着活动数据不能超过一个磁盘扇区，在常见硬件上是 512 字节。提高此限制时请务必小心。
 */
#define PG_CONTROL_MAX_SAFE_SIZE	512

/*
 * pg_control 文件的物理大小。请注意，这比实际使用的大小（即 sizeof(ControlFileData））要大得多。这个想法是使物理大小保持不变，与格式变化无关，以便 ReadControlFile 在查看不兼容文件时将返回适当的错误版本消息，而不是读取错误。
 */
#define PG_CONTROL_FILE_SIZE		8192

#endif							/* PG_CONTROL_H */
